﻿/*
 * Rappelz: Endless Odyssey - The first free open source Rappelz server emulator
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Rappelz.GameServer
{
    public interface QuestEventHandler
    {
        void onStatusChanged(Quest quest, int nOldStatus, int nNewStatus);
        void onProgressChanged(Quest quest, QuestInstance.QuestProgress oldProgress, QuestInstance.QuestProgress newProgress);
        // Function       :     public void QuestEventHandler(const struct StructQuest::QuestEventHandler &)
        // Function       :     public void QuestEventHandler()
        // Function       :     public struct StructQuest::QuestEventHandler & operator=(const struct StructQuest::QuestEventHandler &)

    }

    public class Quest
    {
        public static Dictionary<int, QuestBaseServer> s_hsQuestCode = new Dictionary<int, QuestBaseServer>();
// UserDefinedType: StructQuest
// VTable         :   , Type:  *
// Enum           :   <unnamed-tag>, Type: int
// Data           :     constant 0x408, Constant, Type: int, QUEST_CODE_CHAOS_COLLECT
        public static Quest AllocQuest(QuestEventHandler handler, int nId, int code, int[] status, QuestInstance.QuestProgress progress, int nStartID)
        {
            Quest result = new Quest();
            if (result != null)
            {
                result.m_Instance = new QuestInstance();
                result.m_Handler = handler;
                result.m_bIsNeedUpdateToDB = false;
                result.m_QuestBase = Quest.GetQuestBase(code);
                if(result.m_QuestBase == null)
                    Globals.Log.Error("Invalid Quest Code: {0}", code);
                result.m_Instance.nId = nId;
                result.m_Instance.nStartID = nStartID;
                result.m_Instance.Code = code;
                for(int i = 0; i < 6; ++i)
                {
                    result.m_Instance.nStatus[i] = status[i];
                }
                result.m_Instance.nProgress = progress;
//                result.XSEH::IncreaseAllocCount("StructQuest", 1);
            }
            return result;
        }

        public void FreeQuest()
        {

        }
// Function       :   public void StructQuest(const struct StructQuest &)
// Function       :   public void StructQuest::StructQuest(struct StructQuest::QuestEventHandler *, int, int, int *, enum QuestInstance::QUEST_PROGRESS, int)
// Function       :   public void StructQuest::~StructQuest()
// Function       :   public void SetHandler(struct StructQuest::QuestEventHandler *)

        public static bool RegisterQuestBase(QuestBaseServer q)
        {
            if (!s_hsQuestCode.ContainsKey(q.nCode))
            {
                s_hsQuestCode.Add(q.nCode, q);
            }
            else
            {
                QuestBaseServer qbs = s_hsQuestCode[q.nCode];
                qbs.nEndType = q.nEndType;
                qbs.strAcceptScript = q.strAcceptScript;
                qbs.strClearScript = q.strClearScript;
                qbs.strScript = q.strScript;
                qbs.nIsMagicPointQuest = q.nIsMagicPointQuest;
            }
            return true;
        }

        public int GetQuestCode()
        {
            return m_QuestBase.nCode;
        }

        public static QuestBaseServer GetQuestBase(int code)
        {
            if(s_hsQuestCode.ContainsKey(code))
                return s_hsQuestCode[code];
            return null;
        }

        public QuestBaseServer GetQuestBase()
        {
            return m_QuestBase;
        }

        public QuestInstance GetQuestInstance()
        {
            return m_Instance;
        }

        public int GetQuestID()
        {
            return m_Instance.nId;
        }

        public QuestBase.QuestType GetQuestType()
        {
            return m_QuestBase.nType;
        }

// Function       :   public bool IsRepeatable()
// Function       :   public enum QuestInstance::QUEST_PROGRESS GetProgress()

        public void SetProgress(QuestInstance.QuestProgress progress)
        {
            QuestInstance.QuestProgress old = this.m_Instance.nProgress;
            this.m_Instance.nProgress = progress;
            if (this.m_Handler != null)
                this.m_Handler.onProgressChanged(this, old, progress);
        }

        public int GetValue(int idx)
        {
            int result;

            if (idx > QuestBase.MAX_VALUE_NUMBER-1)
            {
                Globals.Log.Error("StructQuest::GetValue - Invalid Index {0}",idx);
                result = 0;
            }
            else
            {
                result = this.m_QuestBase.nValue[idx];
            }
            return result;
        }


        public int GetStatus(int idx)
        {
            int result;

            if (idx > 5)
            {
                Globals.Log.Error("StructQuest::GetStatus+í+¡ +¯+");
                result = 0;
            }
            else
            {
                result = this.m_Instance.nStatus[idx];
            }
            return result;
        }

        public void UpdateStatus(int idx, int value)
        {
            if (idx > 5)
            {
                Globals.Log.Error("Quest.UpdateStatus: Invalid Index [{0}]", idx);
            }
            else
            {
                int old = this.m_Instance.nStatus[idx];
                this.m_Instance.nStatus[idx] = value;
                if (this.m_Handler != null)
                    this.m_Handler.onStatusChanged(this, old, value);
                this.m_bIsNeedUpdateToDB = true;
            }
        }

        public void IncStatus(int idx, int value)
        {
            if ( idx > 5 )
            {
                Globals.Log.Error("Quest.IncStatus: Invalid Index!");
            }
            else
            {
                int old = this.m_Instance.nStatus[idx];
                this.m_Instance.nStatus[idx] += value;
                if(this.m_Handler != null)
                {
                    this.m_Handler.onStatusChanged(this, old, old + value);
                }
                this.m_bIsNeedUpdateToDB = true;
            }
        }


        public int GetRandomKey(int idx)
        {
            return this.m_Instance.nRandomKey[idx];
        }

        public int GetRandomValue(int idx)
        {
            return this.m_Instance.nRandomValue[idx];
        }
// Function       :   public void StructQuest::SetRandomKey(int, int)
// Function       :   public void StructQuest::SetRandomValue(int, int)

        public bool IsFinishable()
        {

            switch(this.m_QuestBase.nType)
            {
                case QuestBase.QuestType.Misc:
                    break;

                case QuestBase.QuestType.KillTotal:
                    if (this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[1])
                        return true;
                    return false;

                case QuestBase.QuestType.KillIndividual:
                    if (this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[1] 
                        && this.m_Instance.nStatus[1] >= this.m_QuestBase.nValue[3]
                        && this.m_Instance.nStatus[2] >= this.m_QuestBase.nValue[5])
                    {
                        return true;
                    }
                    return false;

                case QuestBase.QuestType.Collect:
                    if (this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[1]
                        && this.m_Instance.nStatus[1] >= this.m_QuestBase.nValue[3]
                        && this.m_Instance.nStatus[2] >= this.m_QuestBase.nValue[5]
                        && this.m_Instance.nStatus[3] >= this.m_QuestBase.nValue[7]
                        && this.m_Instance.nStatus[4] >= this.m_QuestBase.nValue[9])
                        return true;
                    return false;

                case QuestBase.QuestType.HuntItem:
                case QuestBase.QuestType.HuntItemFromAnyMonsters:
                    if (this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[1] 
                        && this.m_Instance.nStatus[1] >= this.m_QuestBase.nValue[3]
                        && this.m_Instance.nStatus[2] >= this.m_QuestBase.nValue[5])
                        return true;
                    return false;

                case QuestBase.QuestType.LearnSkill:
                    if ((this.m_QuestBase.nValue[0] == 0 || this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[1])
                      && (this.m_QuestBase.nValue[2] == 0 || this.m_Instance.nStatus[1] >= this.m_QuestBase.nValue[3])
                      && (this.m_QuestBase.nValue[4] == 0 || this.m_Instance.nStatus[2] >= this.m_QuestBase.nValue[5]))
                    {
                        return true;
                    }
                    return false;

                case QuestBase.QuestType.UpgradeItem:
                    if (this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[1]
                        && this.m_Instance.nStatus[1] >= this.m_QuestBase.nValue[3]
                        && this.m_Instance.nStatus[2] >= this.m_QuestBase.nValue[5])
                            return true;
                    return false;

                case QuestBase.QuestType.Contact:
                    return true;

                case QuestBase.QuestType.JobLevel:
                    if (this.m_Instance.nStatus[0] < this.m_QuestBase.nValue[0])
                        return false;
                    if (this.m_Instance.nStatus[1] < this.m_QuestBase.nValue[1])
                        return false;
                    return true;

                case QuestBase.QuestType.Parameter:
                    if (this.m_QuestBase.nValue[0] == 0
                      || this.m_QuestBase.nValue[1] == 2 && this.m_Instance.nStatus[0] > this.m_QuestBase.nValue[2]
                      || this.m_QuestBase.nValue[1] == 1 && this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[2]
                      || this.m_QuestBase.nValue[1] == 0 && this.m_Instance.nStatus[0] == this.m_QuestBase.nValue[2]
                      || this.m_QuestBase.nValue[1] == -1 && this.m_Instance.nStatus[0] <= this.m_QuestBase.nValue[2]
                      || this.m_QuestBase.nValue[1] == -2 && this.m_Instance.nStatus[0] < this.m_QuestBase.nValue[2])
                    {
                        if (this.m_QuestBase.nValue[3] == 0
                          || this.m_QuestBase.nValue[4] == 2 && this.m_Instance.nStatus[1] > this.m_QuestBase.nValue[5]
                          || this.m_QuestBase.nValue[4] == 1 && this.m_Instance.nStatus[1] >= this.m_QuestBase.nValue[5]
                          || this.m_QuestBase.nValue[4] == 0 && this.m_Instance.nStatus[1] == this.m_QuestBase.nValue[5]
                          || this.m_QuestBase.nValue[4] == -1 && this.m_Instance.nStatus[1] <= this.m_QuestBase.nValue[5]
                          || this.m_QuestBase.nValue[4] == -2 && this.m_Instance.nStatus[1] < this.m_QuestBase.nValue[5])
                        {
                            if (this.m_QuestBase.nValue[6] == 0
                              || this.m_QuestBase.nValue[7] == 2 && this.m_Instance.nStatus[2] > this.m_QuestBase.nValue[8]
                              || this.m_QuestBase.nValue[7] == 1 && this.m_Instance.nStatus[2] >= this.m_QuestBase.nValue[8]
                              || this.m_QuestBase.nValue[7] == 0 && this.m_Instance.nStatus[2] == this.m_QuestBase.nValue[8]
                              || this.m_QuestBase.nValue[7] == -1 && this.m_Instance.nStatus[2] <= this.m_QuestBase.nValue[8]
                              || this.m_QuestBase.nValue[7] == -2 && this.m_Instance.nStatus[2] < this.m_QuestBase.nValue[8])
                                return true;
                        }
                    }
                    return false;

                case QuestBase.QuestType.EndViaScript:
                    if(this.m_QuestBase.nEndType == 3)
                    {
                        if (this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[1]
                            && this.m_Instance.nStatus[1] >= this.m_QuestBase.nValue[3]
                            && this.m_Instance.nStatus[2] >= this.m_QuestBase.nValue[5])
                        {
                            return true;
                        }
                    }
                    return this.m_QuestBase.nEndType < 2;

                case QuestBase.QuestType.RandomKillIndividual:
                    break;
                case QuestBase.QuestType.RandomCollect:
                    break;


            }
            return false;
/*
            bool result;
            v2 = this.m_QuestBase.nType;
            result = false;
            if (this.m_QuestBase.nType > 301)
            {
                v10 = this.m_QuestBase.nType - 501;
                if ( v10 ) // > 501
                {
                    v11 = v10 - 100;
                    if ( !v11 ) // == 601
                    {
                    }
                    v12 = v11 - 300; // >= 901
                    if ( v12 && v12 != 1 // > 901
                      || this.m_Instance.nStatus[0] < this.m_Instance.nRandomValue[0]
                      || this.m_Instance.nStatus[1] < this.m_Instance.nRandomValue[1] )
                        return false;
                    v13 = this.m_Instance.nStatus[2];
                    v6 = __OFSUB__(v13, this.m_Instance.nRandomValue[2]);
                    v5 = v13 - this.m_Instance.nRandomValue[2] < 0;
        LABEL_70:
                    if ( v5 ^ v6 )
                        return false;
                    return true;
                }
                if (this.m_Instance.nStatus[0] < this.m_QuestBase.nValue[0] )
                    return false;
                v8 = this.m_Instance.nStatus[1];
        LABEL_69:
                v6 = __OFSUB__(v8, this.m_QuestBase.nValue[1]);
                v5 = v8 - this.m_QuestBase.nValue[1] < 0;
                if (v5 ^ v6)
                    return false;
                return true;
            }
            if (this.m_QuestBase.nType == 301)
                goto LABEL_15;
            if (this.m_QuestBase.nType == 102)
                goto LABEL_15;
            if (this.m_QuestBase.nType == 103)
            {
                if ( this.m_Instance.nStatus[0] < this.m_QuestBase.nValue[1]
                  || this.m_Instance.nStatus[1] < this.m_QuestBase.nValue[3]
                  || this.m_Instance.nStatus[2] < this.m_QuestBase.nValue[5]
                  || this.m_Instance.nStatus[3] < this.m_QuestBase.nValue[7]
                  || this.m_Instance.nStatus[4] < this.m_QuestBase.nValue[9] )
                    return result;
                v7 = this.m_Instance.nStatus[5];
                v6 = __OFSUB__(v7, this.m_QuestBase.nValue[11]);
                v5 = v7 - this.m_QuestBase.nValue[11] < 0;
                goto LABEL_70;
            }
            if (this.m_QuestBase.nType <= 105)
                return false;
            if (this.m_QuestBase.nType <= 107)
            {
        LABEL_15:
                if (this.m_Instance.nStatus[0] >= this.m_QuestBase.nValue[1] && this.m_Instance.nStatus[1] >= this.m_QuestBase.nValue[3])
                {
                    if (this.m_Instance.nStatus[2] - this.m_QuestBase.nValue[5] <= 0)
                        return false;
                    return true;
                }
                return false;
            }
            return result;
*/
        }

        public static bool IsRandomQuest(int code)
        {
            QuestBase.QuestType qt;

            qt = Quest.GetQuestBase(code).nType;
            return qt == QuestBase.QuestType.RandomCollect || qt == QuestBase.QuestType.RandomKillIndividual;
        }


//         public static NPC GetStartNPC(int code)
//         {
//             qt = Quest.GetQuestBase(code).nType;
//             return qt == QuestBase.QuestType.RandomCollect || qt == QuestBase.QuestType.RandomKillIndividual;
// 
//         }

//         public bool IsRandomQuest()
//         {
// 
//         }
// Function       :   public int GetRewardFavorId()
// Function       :   public int GetRewardHateId()
// Function       :   public int GetFavor()
// Function       :   public __int64 GetExp()
// Function       :   public int GetJP()
// Function       :   public int GetHuntaholicPoint()
// Function       :   public const struct StructGold GetGold()
// Function       :   public const struct QuestBase::Reward & GetDefaultReward()
// Function       :   public const struct QuestBase::Reward & StructQuest::GetOptionalReward(int)
// Function       :   public bool IsNeedUpdateToDB()
// Function       :   public void TurnOffDbUpdateFlag()
// Function       :   public void TurnOnUpdateFlag()
        public QuestBaseServer m_QuestBase;                                     // 0x4
        public QuestEventHandler m_Handler;                                     // 0x8, Member, Type: struct StructQuest::QuestEventHandler *, m_Handler
        public QuestInstance m_Instance;                                        // 0xC
        public bool m_bIsNeedUpdateToDB;                                        // 0x4C

    }
}
